package system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import common.config.datasource.DataSourceConstants;
import common.config.exception.CustomException;
import common.model.po.SysBusinessCode;
import common.model.po.SysBusinessCodeDetail;
import common.model.request.base.PaginationRequest;
import common.service.RedissonCacheService;
import common.util.TreeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import system.mapper.SysBusinessCodeMapper;
import system.model.converter.SysBusinessCodeConverter;
import system.model.request.DeleteBusinessCodeRequest;
import system.model.request.SaveBusinessCodeRequest;
import system.model.request.SearchBusinessCodeRequest;
import system.model.request.UpdateBusinessCodeValidRequest;
import system.service.SysBusinessCodeDetailService;
import system.service.SysBusinessCodeService;
import system.util.BusinessCodeBuilder;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

/**
 * 业务编码服务实现类
 *
 * @author 米泽鹏
 * @since 2022-03-22
 */
@Slf4j
@Service
public class SysBusinessCodeServiceImpl extends ServiceImpl<SysBusinessCodeMapper, SysBusinessCode> implements SysBusinessCodeService {

	@Resource
	private SysBusinessCodeMapper mapper;
	@Resource
	private RedissonCacheService redissonCacheService;
	@Resource
	private SysBusinessCodeDetailService businessCodeDetailService;

	@Override
	public TreeMap<String, List<Tree<Long>>> getAllBusinessCode() {
		return BusinessCodeBuilder.buildBusinessCodeData(redissonCacheService.getSysBusinessCodeCache(), redissonCacheService.getSysBusinessCodeDetailCache());
	}

	@Override
	public Page<SysBusinessCode> pageBusinessCode(PaginationRequest<SearchBusinessCodeRequest, SysBusinessCode> paginationRequest) {
		LambdaQueryWrapper<SysBusinessCode> queryWrapper = paginationRequest.buildQueryWrapper().lambda();
		queryWrapper.orderByDesc(SysBusinessCode::getId);
		return page(paginationRequest.buildPage(), queryWrapper);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@DS(DataSourceConstants.MASTER_DATASOURCE)
	public void saveBusinessCode(SaveBusinessCodeRequest request) {
		// 校验所有叶子结点的value是否重复
		Set<String> valueSet = Sets.newHashSet();
		List<SaveBusinessCodeRequest.SysBusinessCodeDetailVo> detailList = request.getDetailList();
		TreeUtil.treeIteratorDown(detailList, detailVo -> {
			if (detailVo.getChildren() == null || detailVo.getChildren().isEmpty()) {
				String value = detailVo.getValue();
				Assert.isTrue(!valueSet.contains(value), "业务编码取值【" + value + "】重复");
				valueSet.add(value);
			}
		}, SaveBusinessCodeRequest.SysBusinessCodeDetailVo::getChildren);
		// 判断是否改变了数据库数据，用于指示是否更新缓存
		AtomicBoolean dataChanged = new AtomicBoolean(false);
		// 新增
		Long businessCodeId = request.getId();
		if (businessCodeId == null) {
			// 检查编码是否被使用
			List<SysBusinessCode> checkList = lambdaQuery().eq(SysBusinessCode::getCodeType, request.getCodeType()).list();
			Assert.isTrue(checkList.isEmpty(), "业务编码已被使用");
			// 新增业务编码
			SysBusinessCode businessCode = new SysBusinessCode();
			businessCode.setCodeType(request.getCodeType());
			businessCode.setCodeTypeName(request.getCodeTypeName());
			businessCode.setValid(request.getValid());
			if (businessCode.insert()) {
				dataChanged.set(true);
			}
			// 新增业务编码详情
			// 先插入所有根节点
			for (SaveBusinessCodeRequest.SysBusinessCodeDetailVo detailVo : detailList) {
				SysBusinessCodeDetail businessCodeDetail = SysBusinessCodeConverter.detailVoToPo(detailVo);
				businessCodeDetail.setBusinessCodeId(businessCode.getId());
				if (businessCodeDetail.insert()) {
					dataChanged.set(true);
				}
				detailVo.setId(businessCodeDetail.getId());
			}
			// 再插入所有非根节点
			TreeUtil.treeIteratorDown(detailList, detailVo -> {
				if (detailVo.getId() == null) {
					SysBusinessCodeDetail businessCodeDetail = SysBusinessCodeConverter.detailVoToPo(detailVo);
					businessCodeDetail.setBusinessCodeId(businessCode.getId());
					if (businessCodeDetail.insert()) {
						dataChanged.set(true);
					}
					detailVo.setId(businessCodeDetail.getId());
				}
				// 设置所有非根节点的parentId
				List<SaveBusinessCodeRequest.SysBusinessCodeDetailVo> children = detailVo.getChildren();
				if (CollUtil.isNotEmpty(children)) {
					for (SaveBusinessCodeRequest.SysBusinessCodeDetailVo child : children) {
						child.setParentId(detailVo.getId());
					}
				}
			}, SaveBusinessCodeRequest.SysBusinessCodeDetailVo::getChildren);
		}
		// 更新
		else {
			// 检查编码是否被使用
			Long checkCount = lambdaQuery().eq(SysBusinessCode::getCodeType, request.getCodeType()).ne(SysBusinessCode::getId, businessCodeId).count();
			Assert.isTrue(checkCount == 0L, "业务编码已被使用");
			// 原有的业务编码
			SysBusinessCode originalBusinessCode = getById(businessCodeId);
			if (originalBusinessCode == null) {
				throw new CustomException("业务编码不存在");
			}
			// 原有的业务编码详情
			List<SysBusinessCodeDetail> originalBusinessCodeDetailList = businessCodeDetailService.lambdaQuery().eq(SysBusinessCodeDetail::getBusinessCodeId, businessCodeId).list();
			// 更新业务编码
			SysBusinessCode businessCode = new SysBusinessCode();
			businessCode.setId(businessCodeId);
			businessCode.setCodeType(request.getCodeType());
			businessCode.setCodeTypeName(request.getCodeTypeName());
			businessCode.setValid(request.getValid());
			if (isBusinessCodeChanged(originalBusinessCode, businessCode)) {
				if (businessCode.updateById()) {
					dataChanged.set(true);
				}
			}
			// 更新业务编码详情
			// 先处理所有根节点
			for (SaveBusinessCodeRequest.SysBusinessCodeDetailVo detailVo : detailList) {
				SysBusinessCodeDetail businessCodeDetail = SysBusinessCodeConverter.detailVoToPo(detailVo);
				businessCodeDetail.setBusinessCodeId(businessCodeId);
				if (detailVo.getId() == null) {
					if (businessCodeDetail.insert()) {
						dataChanged.set(true);
					}
					detailVo.setId(businessCodeDetail.getId());
				} else {
					SysBusinessCodeDetail originalBusinessCodeDetail = originalBusinessCodeDetailList.stream().filter(sysBusinessCodeDetail -> sysBusinessCodeDetail.getId().equals(detailVo.getId())).findFirst().orElseThrow(() -> new CustomException("业务编码详情不存在"));
					if (isBusinessCodeDetailChanged(originalBusinessCodeDetail, businessCodeDetail)) {
						if (businessCodeDetail.updateById()) {
							dataChanged.set(true);
						}
					}
				}
			}
			// 再处理所有非根节点
			TreeUtil.treeIteratorDown(detailList, detailVo -> {
				SysBusinessCodeDetail businessCodeDetail = SysBusinessCodeConverter.detailVoToPo(detailVo);
				businessCodeDetail.setBusinessCodeId(businessCodeId);
				if (detailVo.getId() == null) {
					if (businessCodeDetail.insert()) {
						dataChanged.set(true);
					}
					detailVo.setId(businessCodeDetail.getId());
				} else {
					// 根节点已经处理过，跳过
					boolean isRoot = detailVo.getParentId() != null && detailVo.getParentId() == 0L;
					if (!isRoot) {
						SysBusinessCodeDetail originalBusinessCodeDetail = originalBusinessCodeDetailList.stream().filter(sysBusinessCodeDetail -> sysBusinessCodeDetail.getId().equals(detailVo.getId())).findFirst().orElseThrow(() -> new CustomException("业务编码详情不存在"));
						if (isBusinessCodeDetailChanged(originalBusinessCodeDetail, businessCodeDetail)) {
							if (businessCodeDetail.updateById()) {
								dataChanged.set(true);
							}
						}
					}
				}
				// 设置所有非根节点的parentId
				List<SaveBusinessCodeRequest.SysBusinessCodeDetailVo> children = detailVo.getChildren();
				if (CollUtil.isNotEmpty(children)) {
					for (SaveBusinessCodeRequest.SysBusinessCodeDetailVo child : children) {
						child.setParentId(detailVo.getId());
					}
				}
			}, SaveBusinessCodeRequest.SysBusinessCodeDetailVo::getChildren);
			// 删除所有被移除的业务编码详情
			Set<Long> originalBusinessCodeDetailIdSet = originalBusinessCodeDetailList.stream().map(SysBusinessCodeDetail::getId).collect(Collectors.toSet());
			Set<Long> currentBusinessCodeDetailIdSet = Sets.newHashSet();
			TreeUtil.treeIteratorDown(detailList, detailVo -> currentBusinessCodeDetailIdSet.add(detailVo.getId()), SaveBusinessCodeRequest.SysBusinessCodeDetailVo::getChildren);
			Set<Long> removedBusinessCodeDetailIdSet = originalBusinessCodeDetailIdSet.stream().filter(id -> !currentBusinessCodeDetailIdSet.contains(id)).collect(Collectors.toSet());
			if (!removedBusinessCodeDetailIdSet.isEmpty()) {
				if (businessCodeDetailService.removeBatchByIds(removedBusinessCodeDetailIdSet)) {
					dataChanged.set(true);
				}
			}
		}
		// 更新缓存
		if (dataChanged.get()) {
			redissonCacheService.cacheSysBusinessCode().throwExceptionIfFailed();
		}
	}

	private boolean isBusinessCodeChanged(SysBusinessCode one, SysBusinessCode another) {
		return !one.getCodeType().equals(another.getCodeType()) || !one.getCodeTypeName().equals(another.getCodeTypeName()) || !one.getValid().equals(another.getValid());
	}

	private boolean isBusinessCodeDetailChanged(SysBusinessCodeDetail one, SysBusinessCodeDetail another) {
		return !one.getParentId().equals(another.getParentId()) || !one.getValue().equals(another.getValue()) || !one.getLabel().equals(another.getLabel()) || !one.getSort().equals(another.getSort());
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@DS(DataSourceConstants.MASTER_DATASOURCE)
	public void deleteBusinessCode(DeleteBusinessCodeRequest request) {
		List<SysBusinessCode> businessCodeList = lambdaQuery().eq(SysBusinessCode::getCodeType, request.getCodeType()).list();
		if (!businessCodeList.isEmpty()) {
			// 删除业务编码
			Set<Long> businessCodeIdSet = businessCodeList.stream().map(SysBusinessCode::getId).collect(Collectors.toSet());
			boolean businessCodeRemoved = removeBatchByIds(businessCodeIdSet);
			// 删除业务编码详情
			boolean businessCodeDetailRemoved = businessCodeDetailService.lambdaUpdate().in(SysBusinessCodeDetail::getBusinessCodeId, businessCodeIdSet).remove();
			// 更新缓存
			if (businessCodeRemoved || businessCodeDetailRemoved) {
				redissonCacheService.cacheSysBusinessCode().throwExceptionIfFailed();
			}
		}
	}

	@Override
	public List<Tree<Long>> getAllBusinessCodeById(Long businessCodeId) {
		List<SysBusinessCodeDetail> businessCodeDetailList = businessCodeDetailService.lambdaQuery().eq(SysBusinessCodeDetail::getBusinessCodeId, businessCodeId).list();
		return BusinessCodeBuilder.buildBusinessCodeDetailData(businessCodeDetailList, false);
	}

	@Override
	@DS(DataSourceConstants.MASTER_DATASOURCE)
	public Boolean updateBusinessCodeValid(UpdateBusinessCodeValidRequest request) {
		boolean updated = lambdaUpdate().set(SysBusinessCode::getValid, request.getValid()).set(SysBusinessCode::getUpdateTime, new Date()).eq(SysBusinessCode::getCodeType, request.getCodeType()).update();
		if (updated) {
			// 更新缓存
			redissonCacheService.cacheSysBusinessCode().throwExceptionIfFailed();
		}
		return updated;
	}

}
